#include "StdAfx.h"
#include "angle_box_2.h"
#include "box_2.h"
#include "segment_2.h"
#include "boost\foreach.hpp"
#include <limits>
#include <algorithm>

point_2 angle_box_2::top_left(void) const
{
  double x = -half_width;
  double y = half_height;

  return point_2(center_x + x*kCos[angle] - y*kSin[angle],
    center_y - x*kSin[angle] - y*kCos[angle]);
}

point_2 angle_box_2::top_right(void) const
{
  double x = half_width;
  double y = half_height;

  return point_2(center_x + x*kCos[angle] - y*kSin[angle],
    center_y - x*kSin[angle] - y*kCos[angle]);
}

point_2 angle_box_2::bottom_left(void) const
{
  double x = -half_width;
  double y = -half_height;

  return point_2(center_x + x*kCos[angle] - y*kSin[angle],
    center_y - x*kSin[angle] - y*kCos[angle]);
}

point_2 angle_box_2::bottom_right(void) const
{
  double x = half_width;
  double y = -half_height;

  return point_2(center_x + x*kCos[angle] - y*kSin[angle],
    center_y - x*kSin[angle] - y*kCos[angle]);
}

bool angle_box_2::within(const point_2& point) const
{
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  vector_2 vec10 = ~(vtx[1] - vtx[0]);
  vector_2 vecp0 = point  - vtx[0];
  vector_2 vec23 = ~(vtx[2] - vtx[3]);
  vector_2 vecp3 = point  - vtx[3];

  bool b0 = (vec10*vecp0)*(vec23*vecp3)<0;

  vector_2 vec21 = ~(vtx[2] - vtx[1]);
  vector_2 vecp1 = point  - vtx[1];
  vector_2 vec30 = ~(vtx[3] - vtx[0]);
  vecp0 = point  - vtx[0];

  bool b1 = (vec21*vecp1)*(vec30*vecp0)<0;

  return b0 && b1;
}


bool angle_box_2::adjacent(const point_2& point, int tolerance) const
{
  point_2 top_left_point     = top_left();
  point_2 top_right_point    = top_right();
  point_2 bottom_left_point  = bottom_left();
  point_2 bottom_right_point = bottom_right();

  segment_2_ref top_segment(top_left_point,       top_right_point);
  segment_2_ref bottom_segment(bottom_left_point, bottom_right_point);
  segment_2_ref left_segment(top_left_point,      bottom_left_point);
  segment_2_ref right_segment(top_right_point,    bottom_right_point);

  return top_segment.adjacent(point, tolerance) ||
    bottom_segment.adjacent(point, tolerance) ||
    left_segment.adjacent(point, tolerance) ||
    right_segment.adjacent(point, tolerance);
}


bool angle_box_2::intersects(const box_2& box) const
{
  return box.within(top_left()) || box.within(top_right()) ||
    box.within(bottom_left()) || box.within(bottom_right());
}

box_2 angle_box_2::bounds(void) const
{
  boost::array<point_2, 4> vertexes = {top_left(), top_right(),
    bottom_left(), bottom_right()};

  double left    = std::numeric_limits<double>::max();
  double right   = std::numeric_limits<double>::min();
  double top     = std::numeric_limits<double>::max();
  double bottom  = std::numeric_limits<double>::min();

  BOOST_FOREACH(const point_2& vertex, vertexes)
  {
    left    = std::min(left, vertex.x);
    right   = std::max(left, vertex.x);
    top     = std::min(left, vertex.y);
    bottom  = std::max(left, vertex.y);
  }

  return box_2(left, top, right, bottom);
}

double angle_box_2::move_handle_to(const point_2& vertex0,
  const point_2& vertex1,
  double length0,
  point_2& vertex3,
  point_2& vertex2,
  double length1,
  const point_2& pt)
{
  vector_2  vec1 = vertex3 - vertex0;
  vector_2  vec2 = pt      - vertex0;
  double    dist = length1;

  if (false == vec1.obtuse(vec2)) {
    dist = pt.distance(vertex0, vertex1, length0);
    vector_2	Vl(dist, dist);
    double    t	= 1.0;

    if(length1 != 0.0) {
      t	  = dist/length1;
      Vl	= vec1;
    } else if(length0 != 0.0) {
      t		= dist/length0;
      Vl = ~(vertex1-vertex0);
    }

    vertex3 = vertex0 + t*Vl;
    vertex2 = vertex1 + t*Vl;
  }

  return dist;
}

void angle_box_2::move_east_handle_to(const point_2& pt)
{
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_width = move_handle_to(vtx[0], vtx[1], half_height*2.0,
    vtx[3], vtx[2], half_width*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_north_handle_to(const point_2& pt)
{ 
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_height = move_handle_to(vtx[1], vtx[2], half_width*2.0,
    vtx[0], vtx[3], half_height*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_west_handle_to(const point_2& pt)
{ 
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_width = move_handle_to(vtx[2], vtx[3], half_height*2.0,
    vtx[1], vtx[0], half_width*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_south_handle_to(const point_2& pt)
{ 
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_height = move_handle_to(vtx[3], vtx[0], half_width*2.0,
    vtx[2], vtx[1], half_height*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_north_east_handle_to(const point_2& pt)
{
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_height = move_handle_to(vtx[1], vtx[2], half_width*2.0,
    vtx[0], vtx[3], half_height*2.0, pt)/2;
  half_width = move_handle_to(vtx[0], vtx[1], half_height*2.0,
    vtx[3], vtx[2], half_width*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_north_west_handle_to(const point_2& pt)
{
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_height = move_handle_to(vtx[1], vtx[2], half_width*2.0,
    vtx[0], vtx[3], half_height*2.0, pt)/2;
  half_width = move_handle_to(vtx[2], vtx[3], half_height*2.0,
    vtx[1], vtx[0], half_width*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_south_west_handle_to(const point_2& pt)
{
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_height = move_handle_to(vtx[3], vtx[0], half_width*2.0,
    vtx[2], vtx[1], half_height*2.0, pt)/2;
  half_width = move_handle_to(vtx[2], vtx[3], half_height*2.0,
    vtx[1], vtx[0], half_width*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_south_east_handle_to(const point_2& pt)
{
  point_2 vtx[4] = { top_left(), bottom_left(), bottom_right(), top_right() };

  half_height = move_handle_to(vtx[3], vtx[0], half_width*2.0,
    vtx[2], vtx[1], half_height*2.0, pt)/2;
  half_width = move_handle_to(vtx[0], vtx[1], half_height*2.0,
    vtx[3], vtx[2], half_width*2.0, pt)/2;

  point_2 center = (vtx[0]+vtx[2])/2.0;
  center_x = center.x;
  center_y = center.y;
}

void angle_box_2::move_top_handle_to(const point_2& pt)
{
  vector_2 vec = pt - point_2(center_x, center_y);
  angle = angle_math::normalize((vec.angle()-90));
}

void angle_box_2::move_bottom_handle_to(const point_2& pt)
{
  vector_2 vec = pt - point_2(center_x, center_y);
  angle = angle_math::normalize((vec.angle()-270));
}

void angle_box_2::move_left_handle_to(const point_2& pt)
{
  vector_2 vec = pt - point_2(center_x, center_y);
  angle = angle_math::normalize((vec.angle()-180));
}

void angle_box_2::move_right_handle_to(const point_2& pt)
{
  vector_2 vec = pt - point_2(center_x, center_y);
  angle = vec.angle();
}
